﻿Friend Class cCutOperationTarget

    Friend Enum eCutOperationStatus
        ecosUntested
        ecosHasNoNeighbours
        ecosHasNearbyNeighbours
        ecosRemoved
        ecosProcessed
    End Enum

    Public Property CutOperationStatus As eCutOperationStatus

End Class


Friend Class cFirePerimeterPathNode
    Inherits cCutOperationTarget

    Implements IFirePerimeterPathNode

    Private mCurrentLocation As cMetricGridCoordinate
    Private mIsNextMoveCalculated As Boolean
    Private mNextMoveDirection As cDirectionAngle
    Private mNextMoveSpeed As Double
    Private mIsMyAngleCalculated As Boolean
    Private mMyLineAngle As Double
    Private mNextNodeInPath As cFirePerimeterPathNode
    Private mPreviousNodeInPath As cFirePerimeterPathNode
    Private mArrayIndexBaseOneForPathSkimmer As Integer

    Friend Shared Function CreateForLocation(ByVal LocationX As Double, ByVal LocationY As Double) As cFirePerimeterPathNode

        Dim NewNode As cFirePerimeterPathNode

        NewNode = New cFirePerimeterPathNode
        NewNode.mCurrentLocation = New cMetricGridCoordinate(LocationX, LocationY)

        Return NewNode

    End Function

    Friend Function Clone() As cFirePerimeterPathNode

        Dim NewNode As cFirePerimeterPathNode

        NewNode = cFirePerimeterPathNode.CreateForLocation(mCurrentLocation.LocationX, mCurrentLocation.LocationY)
        NewNode.AssignNextNodeInPath(mNextNodeInPath)
        NewNode.AssignPreviousNodeInPath(mPreviousNodeInPath)

        Return NewNode

    End Function

    Friend Function IsNeighbour(ByVal OtherNode As cFirePerimeterPathNode) As Boolean

        If OtherNode Is mNextNodeInPath Then
            Return True
        ElseIf OtherNode Is mPreviousNodeInPath Then
            Return True
        Else
            Return False
        End If

    End Function

    Friend Function GetNextMoveSpeed(ByVal SpreadPotentialField As ISpreadPotentialField) As Double

        CalculateNextMoveDirectionAndSpeedIfNeeded(SpreadPotentialField)

        Return mNextMoveSpeed

    End Function

    Private Sub CalculateNextMoveDirectionAndSpeedIfNeeded(ByVal SpreadPotentialField As ISpreadPotentialField)

        Dim DeltaX As Double
        Dim DeltaY As Double

        'The move direction is perpendicular to the line formed between the neighbouring nodes.

        If Not mIsNextMoveCalculated Then
            DeltaX = mNextNodeInPath.MetricLocationX - mPreviousNodeInPath.MetricLocationX
            DeltaY = mNextNodeInPath.MetricLocationY - mPreviousNodeInPath.MetricLocationY
            mNextMoveDirection = cDirectionAngle.CreateByCartesianOffset(DeltaX, DeltaY)
            mNextMoveDirection.AddDegrees(-90)

            With mCurrentLocation
                mNextMoveSpeed = SpreadPotentialField.GetDirectionalSpreadPotentialForLocation(.LocationX, .LocationY, mNextMoveDirection.Degrees)
            End With

            mIsNextMoveCalculated = True
        End If

    End Sub

    Friend Sub AdvanceNodePosition(ByVal SimulationStep As TimeSpan, ByVal SpreadPotentialField As ISpreadPotentialField, ByVal FireModelDirection As eFireModelDirection)

        Dim DeltaX As Double
        Dim DeltaY As Double
        Dim SpreadTimeSeconds As Double
        Dim SpreadMagnitude As Double

        CalculateNextMoveDirectionAndSpeedIfNeeded(SpreadPotentialField)

        'This is for running backwards
        If FireModelDirection = eFireModelDirection.efmdBackwards Then
            mNextMoveSpeed = -mNextMoveSpeed
        End If

        SpreadTimeSeconds = SimulationStep.TotalSeconds
        SpreadMagnitude = mNextMoveSpeed * SpreadTimeSeconds

        DeltaX = SpreadMagnitude * mNextMoveDirection.Cosine
        DeltaY = SpreadMagnitude * mNextMoveDirection.Sine

        With mCurrentLocation
            mCurrentLocation = New cMetricGridCoordinate(.LocationX + DeltaX, .LocationY + DeltaY)
        End With

        mIsNextMoveCalculated = False
        mIsMyAngleCalculated = False

    End Sub

    Friend Function IsDistanceToNextNodeGreaterThan(ByVal TestDistance As Double) As Boolean

        Dim Dx As Double
        Dim Dy As Double
        Dim Interdistance As Double
        Dim GreaterThan As Boolean

        GreaterThan = False

        Dx = Math.Abs(mNextNodeInPath.CurrentLocation.LocationX - mCurrentLocation.LocationX)
        Dy = Math.Abs(mNextNodeInPath.CurrentLocation.LocationY - mCurrentLocation.LocationY)
        Interdistance = (Dx * Dx) + (Dy * Dy)
        TestDistance = TestDistance * TestDistance
        If Interdistance > TestDistance Then
            GreaterThan = True
        End If

        Return GreaterThan

    End Function

    Friend Function IsDistanceToAnotherNodeLessThan(ByVal OtherNode As cFirePerimeterPathNode, ByVal TestDistance As Double) As Boolean

        Dim Dx As Double
        Dim Dy As Double
        Dim Interdistance As Double
        Dim LessThan As Boolean

        LessThan = False

        Dx = Math.Abs(OtherNode.mCurrentLocation.LocationX - mCurrentLocation.LocationX)
        If Dx < TestDistance Then
            Dy = Math.Abs(OtherNode.mCurrentLocation.LocationY - mCurrentLocation.LocationY)
            If Dy < TestDistance Then
                Interdistance = (Dx * Dx) + (Dy * Dy)
                TestDistance = TestDistance * TestDistance
                If Interdistance < TestDistance Then
                    LessThan = True
                End If
            End If
        End If

        Return LessThan

    End Function

    Friend Function GetHalfWayPointToNextNeighbour() As cMetricGridCoordinate

        Dim Interdistance As Double
        Dim LocationX As Double
        Dim LocationY As Double
        Dim NewLocation As cMetricGridCoordinate

        NewLocation = Nothing

        If Not mNextNodeInPath Is Nothing Then

            Interdistance = NextNodeInPath.MetricLocationX - mCurrentLocation.LocationX
            Interdistance = Interdistance / 2
            LocationX = mCurrentLocation.LocationX + Interdistance

            Interdistance = NextNodeInPath.MetricLocationY - mCurrentLocation.LocationY
            Interdistance = Interdistance / 2
            LocationY = mCurrentLocation.LocationY + Interdistance

            NewLocation = New cMetricGridCoordinate(LocationX, LocationY)

        End If

        Return NewLocation

    End Function

    Friend Function GetAngleFormedWithNeighboursDegrees() As Double

        If Not mIsMyAngleCalculated Then
            mMyLineAngle = GetAngleFormedWithTwoOtherNodesDegrees(mPreviousNodeInPath, mNextNodeInPath)
            mIsMyAngleCalculated = True
        End If

        Return mMyLineAngle

    End Function

    Friend Function GetTurnAngleDegrees() As Double

        Return 180 - GetAngleFormedWithNeighboursDegrees()

    End Function

    Friend Function GetAngleFormedWithTwoOtherNodesDegrees(ByVal FirstNode As IFirePerimeterPathNode, ByVal SecondNode As IFirePerimeterPathNode) As Double

        Dim DeltaX As Double
        Dim DeltaY As Double
        Dim DirectionToPreviousNode As cDirectionAngle
        Dim DirectionToNextNode As cDirectionAngle
        Dim AngleDifferenceDegrees As Double

        DeltaX = FirstNode.MetricLocationX - mCurrentLocation.LocationX
        DeltaY = FirstNode.MetricLocationY - mCurrentLocation.LocationY
        DirectionToPreviousNode = cDirectionAngle.CreateByCartesianOffset(DeltaX, DeltaY)

        DeltaX = SecondNode.MetricLocationX - mCurrentLocation.LocationX
        DeltaY = SecondNode.MetricLocationY - mCurrentLocation.LocationY
        DirectionToNextNode = cDirectionAngle.CreateByCartesianOffset(DeltaX, DeltaY)

        AngleDifferenceDegrees = DirectionToPreviousNode.Degrees - DirectionToNextNode.Degrees
        If AngleDifferenceDegrees < 0 Then
            AngleDifferenceDegrees = AngleDifferenceDegrees + 360
        End If

        Return AngleDifferenceDegrees

    End Function

    Friend ReadOnly Property CurrentLocation As cMetricGridCoordinate
        Get
            Return mCurrentLocation
        End Get
    End Property

    Friend Sub AssignPreviousNodeInPath(ByVal NewNeighbour As cFirePerimeterPathNode)

        mPreviousNodeInPath = NewNeighbour

    End Sub

    Friend Sub AssignNextNodeInPath(ByVal NewNeighbour As cFirePerimeterPathNode)

        mNextNodeInPath = NewNeighbour

    End Sub

    Friend Function GetDistanceToPreviousNodeInPath() As Double

        Return GetDirectDistanceToSomeOtherNode(mPreviousNodeInPath)

    End Function

    Friend Function GetDistanceToNextNodeInPath() As Double

        Return GetDirectDistanceToSomeOtherNode(mNextNodeInPath)

    End Function

    Friend ReadOnly Property MetricLocationX As Double Implements IFirePerimeterPathNode.MetricLocationX
        Get
            Return mCurrentLocation.LocationX
        End Get
    End Property

    Friend ReadOnly Property MetricLocationY As Double Implements IFirePerimeterPathNode.MetricLocationY
        Get
            Return mCurrentLocation.LocationY
        End Get
    End Property

    Friend ReadOnly Property NextNodeInPath As cFirePerimeterPathNode
        Get
            Return mNextNodeInPath
        End Get
    End Property

    Friend ReadOnly Property PreviousNodeInPath As cFirePerimeterPathNode
        Get
            Return mPreviousNodeInPath
        End Get
    End Property

    Friend Function GetDirectDistanceToSomeOtherNode(ByVal WhichNode As cFirePerimeterPathNode) As Double

        Dim DeltaX As Double
        Dim DeltaY As Double
        Dim Distance As Double

        With WhichNode
            DeltaX = Math.Abs(.MetricLocationX - mCurrentLocation.LocationX)
            DeltaY = Math.Abs(.MetricLocationY - mCurrentLocation.LocationY)
        End With

        Distance = DeltaX ^ 2
        Distance = Distance + DeltaY ^ 2
        Distance = Distance ^ 0.5

        Return Distance

    End Function

    Private ReadOnly Property InternalNextNode As IFirePerimeterPathNode Implements IFirePerimeterPathNode.NextNode
        Get
            Return mNextNodeInPath
        End Get
    End Property


    Friend Property ArrayIndexBaseOneForPathSkimmer As Integer
        Get
            Return mArrayIndexBaseOneForPathSkimmer
        End Get
        Set(value As Integer)
            mArrayIndexBaseOneForPathSkimmer = value
        End Set
    End Property

End Class
